//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "cthread.h"
#include "helper.h"
#include <aura.h>

#define AttachThread(this) GET_CPROCESS()->Helper_AttachThread(this)
#define DetachThread(this) GET_CPROCESS()->Helper_DetachThread(this)

typedef void (*ElThreadEntry)(
        /* [in] */ PThreadMain userEntry,
        /* [in] */ PVoid pArgs);

static
ECode _CThread_Entry(PVoid pArgs)
{
    CThread * pThread = static_cast<CThread *>(pArgs);
    Address userEntry = 0;
    Address userArgs  = 0;
    Address elEntry   = 0;

    pThread->Helper_GetEntries(&elEntry, &userEntry, &userArgs);
    Helper_TlsZone_SetCurrent(pThread->Helper_GetTlsZone()); // todo if.
    Helper_Thread_SetCurrent(static_cast<IThread *>(pThread));

    ((ElThreadEntry)elEntry)((PThreadMain)userEntry, (PVoid)userArgs);

    return NOERROR;
}

CThread::~CThread()
{
    if (NULL != m_hLock) {
        AuraCriticalSection_Destroy(m_hLock);
        m_hLock = NULL;
    }
    if (NULL != m_hThread) {
        AuraThread_Destroy(m_hThread);
        m_hThread = NULL;
    }
}

ECode CThread::Constructor()  // for main thread.
{
    ECode ec = NOERROR;

    ec = AuraCriticalSection_Create(&m_hLock);
    if (FAILED(ec)) goto E_FAIL_EXIT;

    ec = Helper_TlsZone_Create(&m_pTlsZone);
    if (FAILED(ec)) goto E_FAIL_EXIT;

    ec = Helper_TlsZone_SetCurrent(m_pTlsZone);
    if (FAILED(ec)) goto E_FAIL_EXIT;

    m_threadId = AuraThread_GetCurrentId();
    m_hThread  = AuraThread_GetCurrent();

    m_threadState = ThreadState_Running;

    AttachThread(this);
    AuraSystem_GetTime((Aura_SystemTime *)&m_startTime);

    return NOERROR;

E_FAIL_EXIT:
    if (NULL != m_pTlsZone) {
        Helper_TlsZone_Destroy(m_pTlsZone);
        m_pTlsZone = NULL;
    }
    if (NULL != m_hLock) {
        AuraCriticalSection_Destroy(m_hLock);
        m_hLock = NULL;
    }
    return ec;
}

ECode CThread::Constructor(
        /* [in] */ Address entry,
        /* [in] */ Address userEntry,
        /* [in] */ Address userArgs,
        /* [in] */ ThreadCreationFlags flags)
{
    ECode ec = NOERROR;
    PVoid pArgs = (PVoid *)this;

    m_elEntry   = entry;
    m_userEntry = userEntry;
    m_userArgs  = userArgs;

    ec = AuraCriticalSection_Create(&m_hLock);
    if (FAILED(ec)) goto E_FAIL_EXIT;

    ec = Helper_TlsZone_Clone(
                    Helper_TlsZone_GetCurrent(),
                    &m_pTlsZone);
    if (FAILED(ec)) goto E_FAIL_EXIT;

    AttachThread(this);
    ec = AuraThread_Create((PAuraThreadMain)_CThread_Entry,
                    (PVoid)pArgs,
                    ThreadCreationFlag_Unstarted,
                    &m_hThread,
                    (Aura_ThreadId *)&m_threadId);
    if (FAILED(ec)) goto E_FAIL_EXIT;

    if (ThreadCreationFlag_Unstarted != flags) {
        AuraSystem_GetTime((Aura_SystemTime *)&m_startTime);
        m_threadState = ThreadState_Running;
        ec = AuraThread_Resume(m_hThread);
        if (FAILED(ec)) goto E_FAIL_EXIT;
    }

    return NOERROR;

E_FAIL_EXIT:
    if (NULL != m_pTlsZone) {
        Helper_TlsZone_Destroy(m_pTlsZone);
        m_pTlsZone = NULL;
    }
    if (NULL != m_hLock) {
        AuraCriticalSection_Destroy(m_hLock);
        m_hLock = NULL;
    }
    DetachThread(this);
    return ec;
}

PInterface CThread::Probe(
    /* [in] */ REIID riid)
{
    if (riid == EIID_IInterface) {
        return (PInterface)(IThread *)this;
    }
    else if (riid == EIID_IThread) {
        return (IThread *)this;
    }
    else if (riid == EIID_CLASS_INFO) {
        return (PInterface)&ECLSID_CThread;
    }
    else if (riid == EIID_INTERFACE_INFO) {
        return (PInterface)&EIID_IThread;
    }
    else if (riid == EIID_IThreadContext) {
        return (IThreadContext *) this;
    }

    return NULL;
}

UInt32 CThread::AddRef()
{
    Int32 nRef = m_Refs.Increment();
    return (UInt32)nRef;
}

UInt32 CThread::Release()
{
    Int32 nRef;

    nRef = m_Refs.Decrement();
    if (nRef == 0) {
        DetachThread(this);
        delete this;
    }
    return (UInt32)nRef;
}

ECode CThread::Quit()
{
    //to be implemented
    return E_NOT_IMPLEMENTED;
}

ECode CThread::Aggregate(
    /* [in] */ AggrType aggrType,
    /* [in] */ PInterface pObject)
{
    return E_NOT_IMPLEMENTED;
}

ECode CThread::GetDomain(
    /* [out] */ PInterface *ppObj)
{
    FixMe("Not Implemented yet.");
    return E_NOT_IMPLEMENTED;
}

ECode CThread::Start()
{
    ECode ec = NOERROR;

    LOCK;
    if (ThreadState_Unstarted != m_threadState) {
        goto E_FAIL_EXIT;
    }
    ec = AuraThread_Resume(m_hThread);
    if (FAILED(ec)) goto E_FAIL_EXIT;

    AuraSystem_GetTime((Aura_SystemTime *)&m_startTime);
    m_threadState = ThreadState_Running;
    UNLOCK;

    return NOERROR;

E_FAIL_EXIT:
    UNLOCK;
    return ec;
}

ECode CThread::Suspend()
{
    ECode ec = NOERROR;

    LOCK;
    switch(m_threadState) {
    case ThreadState_Unstarted:
        ec = E_INVALID_OPERATION;
        break;
    case ThreadState_Finished:
        ec = E_THREAD_ALREADY_FINISHED;
        break;
    case ThreadState_Running:
    case ThreadState_Sleeping:
        m_threadState |= ThreadState_Suspended;
        if (Helper_Thread_IsCurrent(static_cast<IThread *>(this))) {
            UNLOCK;
            return AuraThread_Suspend(m_hThread);
        }
        else {
            ec = AuraThread_Suspend(m_hThread);
            if (FAILED(ec)) {
                m_threadState &= ~ThreadState_Suspended;
                break;
            }
        }
     case ThreadState_Suspended:
     case ThreadState_SleepingSuspended:
         m_nestedSuspend.Increment();
         break;
     default:
            break;
    }
    UNLOCK;

    return ec;
}

ECode CThread::Resume()
{
    ECode ec = NOERROR;

    LOCK;
    if (!(m_threadState & ThreadState_Suspended)) {
        ec = E_INVALID_OPERATION;
        goto E_FAIL_EXIT;
    }

    if (m_nestedSuspend.Decrement() > 0) {
        goto E_SUCCESS_EXIT;
    }

    ec = AuraThread_Resume(m_hThread);
    if (FAILED(ec)) goto E_FAIL_EXIT;

    m_threadState &= ~ThreadState_Suspended;

E_SUCCESS_EXIT:
E_FAIL_EXIT:
    UNLOCK;
    return ec;
}
 
ECode CThread::Join(
    /* [in]  */ Millisecond32 timeOut,
    /* [out] */ WaitResult *pResult)
{
    ECode ec = NOERROR;
    Aura_WaitResult wr;

    if (Helper_Thread_IsCurrent(static_cast<IThread *>(this))) {
        return E_INVALID_OPERATION;
    }

    THREAD_START_SLEEP()
    {
        ec = AuraThread_Join(m_hThread, timeOut, &wr);
    }
    THREAD_END_SLEEP;

    if (FAILED(ec)) goto E_FAIL_EXIT;
    if (NULL != pResult) {
        *pResult = wr;
    }
    return NOERROR;

E_FAIL_EXIT:
    return ec;
}

ECode CThread::Interrupt()
{
    FixMe("Not Supported.");
    return E_NOT_SUPPORTED;
}

ECode CThread::Abort()
{
    IThread * pThread = static_cast<IThread *>(this);

    LOCK;
    if ((ThreadState_Unstarted == m_threadState)
        || (ThreadState_Finished == m_threadState)) {
        m_threadState = ThreadState_Finished;
        UNLOCK;
        return NOERROR;
    }
    m_threadState = ThreadState_Finished;
    m_quitCode = E_THREAD_ABORTED;
    AuraSystem_GetTime((Aura_SystemTime *)&m_quitTime);
    UNLOCK;

    if (Helper_Thread_IsMain(pThread)) {
        GET_CPROCESS()->Helper_ExitCurrent(m_quitCode);
        assert(0);
        return NOERROR;
    }
    else if (Helper_Thread_IsCurrent(pThread)) {
        this->Release(); // dereferenced for itselft.
        Helper_TlsZone_Destroy(m_pTlsZone);
        AuraThread_Quit(m_quitCode);
        assert(0);
        return NOERROR;
    }
    else {
        AuraThread_Abort(m_hThread, m_quitCode);
        this->Release(); // dereferenced for itselft.
        Helper_TlsZone_Destroy(m_pTlsZone);
        return NOERROR;
    }
}

ECode CThread::SetPriority(
    /* [in] */ ThreadPriority priority)
{
    if ((priority < ThreadPriority_Lowest)
	    || (priority > ThreadPriority_Highest)) {
	   	return E_INVALID_ARGUMENT;
	}
	if (ThreadState_Finished == m_threadState) {
        return E_THREAD_ALREADY_FINISHED;
    }

    return AuraThread_SetPriority(m_hThread, priority);
}

ECode CThread::GetPriority(
    /* [out] */ ThreadPriority * pPriority)
{
    if (NULL == pPriority) {
        return E_INVALID_ARGUMENT;
    }

    if (ThreadState_Finished == m_threadState) {
        return E_THREAD_ALREADY_FINISHED;
    }
    return AuraThread_GetPriority(m_hThread, (Aura_ThreadPriority *)pPriority);
}

ECode CThread::GetId(
    /* [out] */ ThreadId * pTid)
{
    if (NULL == pTid) {
        return E_INVALID_ARGUMENT;
    }

    *pTid = m_threadId;
    return NOERROR;
}

ECode CThread::SetName(
    /* [in] */ WString name)
{
    FixMe("Not implemented yet.");
    return E_NOT_IMPLEMENTED;
}

ECode CThread::GetName(
    /* [out] */ WStringBuf * pName)
{
    FixMe("Not implemented yet.");
    return E_NOT_IMPLEMENTED;
}

ECode CThread::GetState(
    /* [out] */ ThreadState * pState)
{
    if (NULL == pState) {
        return E_INVALID_ARGUMENT;
    }

    LOCK;
    *pState = m_threadState;
    UNLOCK;

    return NOERROR;
}

ECode CThread::GetQuitCode(
    /* [out] */ QuitCode * pQuitCode)
{
    if (NULL == pQuitCode) {
        return E_INVALID_ARGUMENT;
    }

    LOCK;
    if (ThreadState_Finished != m_threadState) {
        goto E_FAIL_EXIT;
    }
    UNLOCK;

    *pQuitCode = m_quitCode;
    return NOERROR;

E_FAIL_EXIT:
    UNLOCK;
    return E_THREAD_NOT_STOPPED;
}

ECode CThread::GetStartTime(
    /* [out] */ SystemTime * pTime)
{
    if (NULL == pTime) {
        return E_INVALID_ARGUMENT;
    }

    LOCK;
    if (ThreadState_Unstarted == m_threadState) {
        goto E_FAIL_EXIT;
    }
    *pTime = m_startTime;
    UNLOCK;

    return NOERROR;

E_FAIL_EXIT:
    UNLOCK;
    return E_THREAD_UNSTARTED;
}

ECode CThread::GetQuitTime(
    /* [out] */ SystemTime * pTime)
{
    if (NULL == pTime) {
        return E_INVALID_ARGUMENT;
    }

    LOCK;
    if (ThreadState_Finished != m_threadState) {
        goto E_FAIL_EXIT;
    }
    *pTime = m_startTime;
    UNLOCK;

    return NOERROR;

E_FAIL_EXIT:
    UNLOCK;
    return E_THREAD_NOT_STOPPED;
}

ECode CThread::GetProcessorTimes(
    /* [out] */ Millisecond32 * pUserTime,
    /* [out] */ Millisecond32 * pPrivilegedTime)
{
    if ((NULL == pUserTime)
        && (NULL == pPrivilegedTime)) {
        return E_INVALID_ARGUMENT;
    }

    return AuraThread_GetProcessorTimes(m_hThread,
                     (Aura_Millisecond32 *)pUserTime,
                     (Aura_Millisecond32 *)pPrivilegedTime);
}

ECode CThread::GetContext(
    /* [out] */ ArrayOf<Int32> *pContextArray)
{
    FixMe("Not implemented yet.");
    return E_NOT_IMPLEMENTED;
}

ECode CThread::SetContext(
    /* [in] */ const ArrayOf<Int32> &contextArray)
{
    FixMe("Not implemented yet.");
    return E_NOT_IMPLEMENTED;
}

ECode CThread::GetUserStackTop(
    /* [out] */ Address *pStackTop)
{
    FixMe("Not implemented yet.");
    return E_NOT_IMPLEMENTED;
}

void CThread::Helper_SetState(
    /* [in] */ ThreadState state)
{
    LOCK;
    assert((ThreadState_Running == m_threadState)
        || (ThreadState_Sleeping == m_threadState));
    m_threadState = state;
    UNLOCK;
}

void CThread::Helper_QuitAsCurrent(
    /* [in] */ QuitCode quitCode)
{
    LOCK;
    assert(ThreadState_Finished != m_threadState);
    assert(ThreadState_Unstarted != m_threadState);

    m_threadState = ThreadState_Finished;
    m_quitCode = quitCode;
    AuraSystem_GetTime((Aura_SystemTime *)&m_quitTime);
    UNLOCK;

    Helper_TlsZone_Destroy(m_pTlsZone);
    this->Release();
    AuraThread_Quit(quitCode);
    assert(0);
}

ECode CThread::New(
    /* [out] */ IThread ** ppThread)
{
    CThread * pThread = NULL;
    ECode ec = NOERROR;

    assert(NULL != ppThread);

    pThread = new CThread();
    if (NULL == pThread) {
        ec = E_OUT_OF_MEMORY;
        goto E_FAIL_EXIT;
    }
    ec = pThread->Constructor();
    if (FAILED(ec)) {
        goto E_FAIL_EXIT;
    }
    *ppThread = static_cast<IThread *>(pThread);
    (*ppThread)->AddRef(); // referenced by itself;
    (*ppThread)->AddRef(); // referenced by caller;

    return NOERROR;

E_FAIL_EXIT:
    if (NULL != pThread) {
        delete pThread;
    }
    return ec;
}

ECode CThread::New(
    /* [in] */ Address entry,
    /* [in] */ Address userEntry,
    /* [in] */ Address userArgs,
    /* [in] */ ThreadCreationFlags flags,
    /* [out] */ IThread ** ppThread)
{
    CThread * pThread = NULL;
    ECode ec = NOERROR;

    assert(NULL != ppThread);

    pThread = new CThread();
    if (NULL == pThread) {
        ec = E_OUT_OF_MEMORY;
        goto E_FAIL_EXIT;
    }
    ec = pThread->Constructor(entry, userEntry, userArgs, flags);
    if (FAILED(ec)) {
        goto E_FAIL_EXIT;
    }
    *ppThread = static_cast<IThread *>(pThread);
    (*ppThread)->AddRef(); // referenced by itself;
    (*ppThread)->AddRef(); // referenced by caller;

    return NOERROR;

E_FAIL_EXIT:
    if (NULL != pThread) {
        delete pThread;
    }
    return ec;
}

